//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.zaicheng.cloud.remotecall;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public abstract class DeclarativeContract extends Contract.BaseContract {
    private final List<DeclarativeContract.GuardedAnnotationProcessor> classAnnotationProcessors = new ArrayList();
    private final List<DeclarativeContract.GuardedAnnotationProcessor> methodAnnotationProcessors = new ArrayList();
    private final Map<Class<Annotation>, DeclarativeContract.ParameterAnnotationProcessor<Annotation>> parameterAnnotationProcessors = new HashMap();

    public DeclarativeContract() {
    }

    @Override
    public final List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
        return super.parseAndValidateMetadata(targetType);
    }

    @Override
    protected final void processAnnotationOnClass(MethodMetadata data, Class<?> targetType) {
        List<DeclarativeContract.GuardedAnnotationProcessor> processors = (List)Arrays.stream(targetType.getAnnotations()).flatMap((annotation) -> {
            return this.classAnnotationProcessors.stream().filter((processor) -> {
                return processor.test(annotation);
            });
        }).collect(Collectors.toList());
        if (!processors.isEmpty()) {
            Arrays.stream(targetType.getAnnotations()).forEach((annotation) -> {
                processors.stream().filter((processor) -> {
                    return processor.test(annotation);
                }).forEach((processor) -> {
                    processor.process(annotation, data);
                });
            });
        } else if (targetType.getAnnotations().length == 0) {
            data.addWarning(String.format("Class %s has no annotations, it may affect contract %s", targetType.getSimpleName(), this.getClass().getSimpleName()));
        } else {
            data.addWarning(String.format("Class %s has annotations %s that are not used by contract %s", targetType.getSimpleName(), Arrays.stream(targetType.getAnnotations()).map((annotation) -> {
                return annotation.annotationType().getSimpleName();
            }).collect(Collectors.toList()), this.getClass().getSimpleName()));
        }

    }

    @Override
    protected final void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method) {
        List<DeclarativeContract.GuardedAnnotationProcessor> processors = (List)this.methodAnnotationProcessors.stream().filter((processor) -> {
            return processor.test(annotation);
        }).collect(Collectors.toList());
        if (!processors.isEmpty()) {
            processors.forEach((processor) -> {
                processor.process(annotation, data);
            });
        } else {
            data.addWarning(String.format("Method %s has an annotation %s that is not used by contract %s", method.getName(), annotation.annotationType().getSimpleName(), this.getClass().getSimpleName()));
        }

    }

    @Override
    protected final boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
        List<Annotation> matchingAnnotations = (List)Arrays.stream(annotations).filter((annotation) -> {
            return this.parameterAnnotationProcessors.containsKey(annotation.annotationType());
        }).collect(Collectors.toList());
        if (!matchingAnnotations.isEmpty()) {
            matchingAnnotations.forEach((annotation) -> {
                ((DeclarativeContract.ParameterAnnotationProcessor)this.parameterAnnotationProcessors.getOrDefault(annotation.annotationType(), DeclarativeContract.ParameterAnnotationProcessor.DO_NOTHING)).process(annotation, data, paramIndex);
            });
        } else {
            Parameter parameter = data.method().getParameters()[paramIndex];
            String parameterName = parameter.isNamePresent() ? parameter.getName() : parameter.getType().getSimpleName();
            if (annotations.length == 0) {
                data.addWarning(String.format("Parameter %s has no annotations, it may affect contract %s", parameterName, this.getClass().getSimpleName()));
            } else {
                data.addWarning(String.format("Parameter %s has annotations %s that are not used by contract %s", parameterName, Arrays.stream(annotations).map((annotation) -> {
                    return annotation.annotationType().getSimpleName();
                }).collect(Collectors.toList()), this.getClass().getSimpleName()));
            }
        }

        return false;
    }

    protected <E extends Annotation> void registerClassAnnotation(Class<E> annotationType, DeclarativeContract.AnnotationProcessor<E> processor) {
        this.registerClassAnnotation((annotation) -> {
            return annotation.annotationType().equals(annotationType);
        }, processor);
    }

    protected <E extends Annotation> void registerClassAnnotation(Predicate<E> predicate, DeclarativeContract.AnnotationProcessor<E> processor) {
        this.classAnnotationProcessors.add(new DeclarativeContract.GuardedAnnotationProcessor(predicate, processor));
    }

    protected <E extends Annotation> void registerMethodAnnotation(Class<E> annotationType, DeclarativeContract.AnnotationProcessor<E> processor) {
        this.registerMethodAnnotation((annotation) -> {
            return annotation.annotationType().equals(annotationType);
        }, processor);
    }

    protected <E extends Annotation> void registerMethodAnnotation(Predicate<E> predicate, DeclarativeContract.AnnotationProcessor<E> processor) {
        this.methodAnnotationProcessors.add(new DeclarativeContract.GuardedAnnotationProcessor(predicate, processor));
    }

    protected <E extends Annotation> void registerParameterAnnotation(Class<E> annotation, DeclarativeContract.ParameterAnnotationProcessor<E> processor) {
        this.parameterAnnotationProcessors.put((Class<Annotation>) annotation, (ParameterAnnotationProcessor<Annotation>) processor);
    }

    private class GuardedAnnotationProcessor implements Predicate<Annotation>, DeclarativeContract.AnnotationProcessor<Annotation> {
        private final Predicate<Annotation> predicate;
        private final DeclarativeContract.AnnotationProcessor<Annotation> processor;

        private GuardedAnnotationProcessor(Predicate predicate, DeclarativeContract.AnnotationProcessor processor) {
            this.predicate = predicate;
            this.processor = processor;
        }

        @Override
        public void process(Annotation annotation, MethodMetadata metadata) {
            this.processor.process(annotation, metadata);
        }

        @Override
        public boolean test(Annotation t) {
            return this.predicate.test(t);
        }
    }

    @FunctionalInterface
    public interface ParameterAnnotationProcessor<E extends Annotation> {
        DeclarativeContract.ParameterAnnotationProcessor<Annotation> DO_NOTHING = (ann, data, i) -> {
        };

        void process(E var1, MethodMetadata var2, int var3);
    }

    @FunctionalInterface
    public interface AnnotationProcessor<E extends Annotation> {
        void process(E var1, MethodMetadata var2);
    }
}
